package com.bkitmobile.kesu.appengine.mobile.server;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.logging.Logger;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;

import javax.servlet.ServletException;
import javax.servlet.http.*;

import org.apache.jsp.ah.datastoreViewerBody_jsp;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.json.JSONStringer;

import com.appenginefan.toolkit.common.ServerEndpoint;
import com.appenginefan.toolkit.common.WebConnectionServer;
import com.appenginefan.toolkit.persistence.DatastorePersistence;
import com.appenginefan.toolkit.persistence.Persistence;
import com.bkitmobile.kesu.appengine.storingdata.server.DataStoreManager;
import com.bkitmobile.kesu.appengine.storingdata.server.entity.Message;
import com.bkitmobile.kesu.appengine.storingdata.server.entity.Place;
import com.bkitmobile.kesu.appengine.storingdata.server.entity.User;
import com.bkitmobile.kesu.appengine.storingdata.server.entity.UserConfig;
import com.bkitmobile.kesu.appengine.storingdata.server.entity.UserLocationData;
import com.google.appengine.api.datastore.Key;
import com.google.appengine.repackaged.com.google.common.util.Base64;

public class SocketServlet extends HttpServlet implements WebConnectionServer.Receiver
{
	private Persistence<byte[]> data;
	private WebConnectionServer server;
	private static final Logger log = Logger.getLogger(SocketServlet.class.getName());
	public void doPost(HttpServletRequest req, HttpServletResponse res)
    throws IOException {
		 if (!server.dispatch(this, req, res)) {
		      res.sendError(404);
		    }
	}
	@Override
	  public void receive(
	      WebConnectionServer theServer,
	      ServerEndpoint socket, 
	      String theMessage,
	      HttpServletRequest request) {
		log.info(theMessage);
	    final String name = socket.getProperty("name", null);
	    DataStoreManager db = new DataStoreManager();
	    if (name == null && theMessage.startsWith("<login>")) {
	      socket.setProperty("name", 
	    		  theMessage.substring(
	    				  theMessage.indexOf("<login>") + 7
	      			).trim());
	      User user = db.getUser(socket.getProperty("name", null));
	      try{
	    	  theServer.fromHandle(request, user.getPid()).send("<AnotherLogin>");
	      } catch (Exception e)
	      {
	    	  
	      }
	      List<User> lsUser = db.getFriendsList(user);
	      user.setPid(socket.getHandle());
	      db.updateUser(user);
	      UserLocationData uld = db.getUserLocationData(user.getId());
	      boolean state = false;
	      
	      Set<Key> usersViewInvi = user.getUserViewInvisible();
	      Set<Key> usersManage = uld.getUsersView();
	      String message = "<flogin>"+user.getId()+"<status>"+user.getStatus();
	      for (int i = 0 ; i < lsUser.size() ; i++)
	      {
	    	  try {
	    		  state = user.isAvailable()
	    		  ||usersViewInvi.contains(lsUser.get(i).getKey())
	    		  ||usersManage.contains(lsUser.get(i).getKey())
	    		  ;
	    		 if (state) 
	    		  theServer.fromHandle(request, lsUser.get(i).getPid()).send(message);
	    	  } catch (Exception e)
	    	  {
	    		  continue;
	    	  }
	      }
	    }
	    
	    else {
	    	if (theMessage.startsWith("<chat>"))
	    	{
	    		String dest = theMessage.substring(
	    				theMessage.indexOf("<dest>")+6
	    				, theMessage.indexOf("<message>"));
	    		User uDest = db.getUser(dest);
	    		String store = theMessage.substring(
	    				theMessage.lastIndexOf("<message>"));
	    		String message = "<chat>"+"<from>"+name+store;
	    		try {
		    		theServer.fromHandle(request,uDest.getPid()).send(message);
		    		User uFr = db.getUser(name);
		    		db.deleteMessage(uDest,uFr);
	    			db.addMessage(uFr, uDest, store);
	    		} catch (Exception e)
	    		{
	    			
	    		}
	    	}
	    	else if (theMessage.startsWith("<location>"))
	    	{
	    		String latitude = theMessage.substring(
		    				theMessage.indexOf("<lat>")+5, 
		    				theMessage.indexOf("<long>")
		    				);
	    		String longitude = theMessage.substring(
			    				theMessage.indexOf("<long>")+6
			    				);
	    		String resp = Responses.UpdateLocationResp(name, longitude, latitude);
	    		if (resp != null)
					try {
						socket.send(URLEncoder.encode(resp,"UTF-8"));
					} catch (UnsupportedEncodingException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
	    		// to do send list location near this location
	    	} else if (theMessage.startsWith("<logout>")||theMessage.startsWith("<exit>"))
	    	{
	    		User user = null;
	    		if (theMessage.startsWith("<exit>"))
	    			user = db.getUser(theMessage.substring(
	    				theMessage.indexOf("<exit>")+6
				));  else user = db.getUser(name);		
	    		String message = "<flogout>"+user.getId();
	    		List<User> lsUser = db.getFriendsList(user);
		  	    for (int i = 0 ; i < lsUser.size() ; i++)
		  	    {
		  	    	try {
		  	    	theServer.fromHandle(request, lsUser.get(i).getPid()).send(message);
		  	    	} catch (Exception e)
		  	    	{
		  	    		continue;
		  	    	}
		  	    }
			  	Object[] lsKey = user.getMessages().toArray();
				for (int i = 0 ; i < lsKey.length ; i++)
			    {
			        Message msg = db.getMessage((Key)lsKey[i]);
			        try {
			        	db.deleteMessage((Key)lsKey[i]);
			        } catch (Exception e)
			        {
			        	
			        }
			    }
			  	db.doLogOut(user.getId());
	    	} else if (theMessage.startsWith("<add>"))
	    	{
	    		User userFrom = db.getUser(name);
	    		String dest = theMessage.substring(
	    				theMessage.indexOf("<add>")+5,
	    				theMessage.lastIndexOf("<cmt>")
	    				);
	    		User userDest = db.getUser(dest);
	    		if (userDest == null) 
	    		{
	    			socket.send("<AddNotExist>"+dest);
	    		} else {
		    		String cmt = theMessage.substring(theMessage.lastIndexOf("<cmt>")+5);
		    		int result = Responses.addFriendResp(name, userDest.getId(),cmt);
		    		String message = "<add>"+name+"<cmt>"+cmt;
		    		try {
		    			if (userDest.getPid() != null)
		    				theServer.fromHandle(request, userDest.getPid()).send(message);
		    		} catch (Exception e)
		    		{
		    			
		    		} finally {
		    			socket.send("<AddSuccess>"+userDest.getId());
		    		}
	    		}
	    	} else if (theMessage.startsWith("<accept>"))
	    	{
	    		User userFrom = db.getUser(name);
	    		User userDest = db.getUser(theMessage.substring(
	    				theMessage.indexOf("<accept>")+8
	    				));
	    		Responses.acceptInviteResp(name, userDest.getId());
	    		String str = null;
	    		try {
	    			str = Base64.encode(userFrom.getAvatar().getBytes());
	    		} catch (Exception e){
	    			
	    		}
	    		String message = "<accept>"+name+"<status>"+userFrom.getStatus()
	    			+"<state>"+(userFrom.isAvailable()?"online":"offline")+"<img>"+str;
	    		theServer.fromHandle(request,userDest.getPid()).send(message);
	    		String messResp = "<fadd>"+userDest.getId()+"<status>"+userDest.getStatus()
	    			+"<state>"+(userDest.isAvailable()&&userDest.isLoggedin()?"online":"offline");
	    		socket.send(messResp);
	    	} else if (theMessage.startsWith("<deny>"))
	    	{
	    		User userFrom = db.getUser(name);
	    		User userDest = db.getUser(theMessage.substring(
	    				theMessage.indexOf("<deny>")+6
	    				));
	    		Responses.denyInviteResp(name, userDest.getId());
	    		String message = "<deny>"+name;
	    		try {
	    			theServer.fromHandle(request,userDest.getPid()).send(message);
	    		} catch (Exception e)
	    		{
	    			
	    		}
	    	} else if (theMessage.startsWith("<remove>"))
	    	{
	    		//User userFrom = db.getUser(name);
	    		User userDest = db.getUser(theMessage.substring(
	    				theMessage.indexOf("<remove>")+8
	    				).trim());
	    		db.removeUser(name, userDest.getId());
	    		String message = "<remove>"+name;
	    		try {
	    			if (userDest.getPid()==null) return;
	    			theServer.fromHandle(request,userDest.getPid()).send(message);
	    		} catch (Exception e) 
	    		{
	    			return;
	    		}
	    	}else if (theMessage.startsWith("<changePwd>"))
	    	{
	    		String newPwd = theMessage.substring(
	    				theMessage.indexOf("<changePwd>")+11
	    				).trim();
	    		Responses.changePassword(name, newPwd);
	    	
	    	}else if (theMessage.startsWith("<status>"))
	    	{
	    		String newStt = theMessage.substring(
	    				theMessage.indexOf("<status>")+8,
	    				theMessage.lastIndexOf("<long>")
	    				).trim();
	    		double longitude = Double.parseDouble(theMessage.substring(
	    				theMessage.lastIndexOf("<long>")+6,
	    				theMessage.lastIndexOf("<lat>")
	    				).trim());
	    		double latitude = Double.parseDouble(theMessage.substring(
	    				theMessage.lastIndexOf("<lat>")+5
	    				).trim());
	    		Responses.changeStatus(name, newStt,longitude,latitude);
	    		User user = db.getUser(name);		
	    		List<User> lsUser = db.getFriendsList(user);
	  	      	if (user.isAvailable()){
			  	    for (int i = 0 ; i < lsUser.size() ; i++)
			  	    {
			  	    	try {
			  	    	theServer.fromHandle(request, lsUser.get(i).getPid())
			  	    			.send("<fstatus>"+name+"<status>"+newStt);
			  	    	} catch (Exception e)
			  	    	{
			  	    		continue;
			  	    	}
			  	    }
	  	      	} else {
	  	      		UserLocationData uld =db.getUserLocationData(name);
		  	      	Set<Key> usersViewInvi = user.getUserViewInvisible();
		  	      	Set<Key> usersManage = uld.getUsersView();
		  	      	for (int i = 0 ; i < lsUser.size() ; i++)
			  	    {
			  	    	try {
			  	    	if (usersViewInvi.contains(lsUser.get(i).getKey())
			  	    			||usersManage.contains(lsUser.get(i).getKey()))
			  	    		theServer.fromHandle(request, lsUser.get(i).getPid())
			  	    			.send("<fstatus>"+name+"<status>"+newStt);
			  	    	} catch (Exception e)
			  	    	{
			  	    		continue;
			  	    	}
			  	    }
	  	      	}
	    	} else if (theMessage.startsWith("<state>"))
	    	{
	    		String newState = theMessage.substring(
	    				theMessage.indexOf("<state>")+7
	    				).trim();
	    		User user = db.getUser(name);		
	    		List<User> lsUser = db.getFriendsList(user);
	    		boolean isAvailable = newState.equals("available");
	    		user.setAvailable(isAvailable);
	    		db.updateUser(user);
	    		UserLocationData uld =db.getUserLocationData(name);
    			Set<Key> usersViewInvi = user.getUserViewInvisible();
	  	      	Set<Key> usersManage = uld.getUsersView();
	    		if (isAvailable){
	    			String message = "<flogin>"+user.getId()+"<status>"+user.getStatus();
			  	    for (int i = 0 ; i < lsUser.size() ; i++)
			  	    {
			  	    	try {
			  	    	if(!usersViewInvi.contains(lsUser.get(i).getKey()) 
			  	    			&& !usersManage.contains(lsUser.get(i).getKey()))
			  	    		theServer.fromHandle(request, lsUser.get(i).getPid())
			  	    				.send(message);
			  	    	} catch (Exception e)
			  	    	{
			  	    		continue;
			  	    	}
			  	    }
	    		} else {
	    			String message = "<flogout>"+user.getId();
		  	      	for (int i = 0 ; i < lsUser.size() ; i++)
			  	    {
			  	    	try {
			  	    		if(!usersViewInvi.contains(lsUser.get(i).getKey()) 
				  	    			&& !usersManage.contains(lsUser.get(i).getKey()))
			  	    			theServer.fromHandle(request, lsUser.get(i).getPid())
		  	    				.send(message);
			  	    	} catch (Exception e)
			  	    	{
			  	    		continue;
			  	    	}
			  	    }
	    		}
	    	}
	    	else if (theMessage.startsWith("<editProfile>"))
	    	{
	    		String fName = theMessage.substring(
	    				theMessage.indexOf("<fname>")+7,
	    				theMessage.indexOf("<lname>")
	    				).trim();
	    		String lName = theMessage.substring(
	    				theMessage.indexOf("<lname>")+7,
	    				theMessage.indexOf("<sex>")
	    				).trim();
	    		String sex = theMessage.substring(
	    				theMessage.indexOf("<sex>")+5,
	    				theMessage.indexOf("<bdate>")
	    				).trim();
	    		String bDay = theMessage.substring(
	    				theMessage.indexOf("<bdate>")+7,
	    				theMessage.indexOf("<email>")
	    				).trim();
	    		String email = theMessage.substring(
	    				theMessage.indexOf("<email>")+7,
	    				theMessage.lastIndexOf("<img>")
	    				).trim();
	    		String img = theMessage.substring(theMessage.lastIndexOf("<img>")+5);
	    		Responses.updateMyProfile(name, fName, lName, sex, bDay, email,img);
	    	} else if (theMessage.startsWith("<AllowFollowMe>"))
	    	{
	    		String person = theMessage.substring(
	    				15
	    				).trim();
	    		String result = "<AllowFollowMe>"
	    			+Responses.allowFollow(name, person)+"<u>"+person;
	    		User userFrom = db.getUser(name);
	    		User userDest = db.getUser(person);
	    		String message = "<flogin>"+userFrom.getId()+"<status>"+userFrom.getStatus();
	    		try {
	    		if (!userFrom.isAvailable())
	    			theServer.fromHandle(request, userDest.getPid()).send(message);
	    		} catch (Exception e)
	    		{
	    			
	    		}
	    		socket.send(result);
	    	} else if (theMessage.startsWith("<DisallowFollowMe>"))
	    	{
	    		String person = theMessage.substring(
	    				18
	    				).trim();
	    		String result = "<DisallowFollowMe>"+Responses.disallowFollow(name, person)
	    						+"<u>"+person;
	    		User userFrom = db.getUser(name);
	    		User userDest = db.getUser(person);
	    		String message = "<flogout>"+name;
	    		try {
	    		if (!userFrom.isAvailable())
	    			theServer.fromHandle(request, userDest.getPid()).send(message);
	    		} catch (Exception e)
	    		{
	    			
	    		}
	    		socket.send(result);
	    	} else if (theMessage.startsWith("<AllowSee24>"))
	    	{
	    		String person = theMessage.substring(
	    				12
	    				).trim();
	    		String result = "<AllowSee24>"
	    			+Responses.allowSee24(name, person)+"<u>"+person;
	    		User userFrom = db.getUser(name);
	    		User userDest = db.getUser(person);
	    		String message = "<flogin>"+userFrom.getId()+"<status>"+userFrom.getStatus();
	    		try {
	    		if (!userFrom.isAvailable())
	    			theServer.fromHandle(request, userDest.getPid()).send(message);
	    		} catch (Exception e)
	    		{
	    			
	    		}
	    		socket.send(result);
	    	} else if (theMessage.startsWith("<DisallowSee24>"))
	    	{
	    		String person = theMessage.substring(
	    				15
	    				).trim();
	    		String result = "<DisallowSee24>"
	    			+Responses.disallowSee24(name, person)+"<u>"+person;
	    		User userFrom = db.getUser(name);
	    		User userDest = db.getUser(person);
	    		String message = "<flogout>"+name;
	    		try {
	    		if (!userFrom.isAvailable())
	    			theServer.fromHandle(request, userDest.getPid()).send(message);
	    		} catch (Exception e)
	    		{
	    			
	    		}
	    		socket.send(result);
	    	} else if (theMessage.startsWith("<cmtPlace>"))
	    	{
	    		int lat = Integer.parseInt(theMessage.substring(
	    				theMessage.indexOf("<lat>")+5,
	    				theMessage.indexOf("<lng>")
	    				).trim());
	    		int lng = Integer.parseInt(theMessage.substring(
	    				theMessage.indexOf("<lng>")+5,
	    				theMessage.indexOf("<cmt>")
	    				).trim());
	    		String cmt = theMessage.substring(
	    				theMessage.indexOf("<cmt>")+5
	    				).trim();
	    		db.addPlaceComment(name, lat, lng, cmt);
	    	}else if (theMessage.startsWith("<newloc>"))
	    	{
	    		String namePlace = theMessage.substring(
	    				theMessage.indexOf("<name>")+6,
	    				theMessage.indexOf("<addr>")
	    				).trim();
	    		String addr = theMessage.substring(
	    				theMessage.indexOf("<addr>")+6,
	    				theMessage.indexOf("<type>")
	    				).trim();
	    		String type = theMessage.substring(
	    				theMessage.indexOf("<type>")+6,
	    				theMessage.indexOf("<descr>")
	    				).trim();
	    		String descr = theMessage.substring(
	    				theMessage.indexOf("<descr>")+7,
	    				theMessage.lastIndexOf("<lng>")
	    				).trim();
	    		double lng = Double.parseDouble(theMessage.substring(
	    				theMessage.indexOf("<lng>")+5,
	    				theMessage.lastIndexOf("<lat>")
	    				).trim());
	    		double lat = Double.parseDouble(theMessage.substring(
	    				theMessage.indexOf("<lat>")+5,
	    				theMessage.lastIndexOf("<privacy>")
	    				).trim());
	    		int privacy = Integer.parseInt(theMessage.substring(
	    				theMessage.lastIndexOf("<privacy>")+9
	    				));
	    		db.createNewPlace(name, namePlace, type, descr, addr, lng, lat,true,privacy);
	    		
	    	} 
	    	//<editLoc><lat>latE6<lng>lngE6<name>name<addr>addr<info>info
	    	else if (theMessage.startsWith("<editLoc>"))
	    	{
	    		int lat = Integer.parseInt(theMessage.substring(
	    				theMessage.indexOf("<lat>")+5,
	    				theMessage.lastIndexOf("<lng>")
	    				).trim());
	    		int lng = Integer.parseInt(theMessage.substring(
	    				theMessage.lastIndexOf("<lng>")+5,
	    				theMessage.indexOf("<name>")
	    				).trim());
	    		String pName = theMessage.substring(
	    				theMessage.lastIndexOf("<name>")+6,
	    				theMessage.indexOf("<addr>")
	    				).trim();
	    		String addr = theMessage.substring(
	    				theMessage.lastIndexOf("<addr>")+6,
	    				theMessage.indexOf("<info>")
	    				).trim();
	    		String info = theMessage.substring(
	    				theMessage.indexOf("<info>")+6
	    				).trim();
	    		Place p = db.getPlace(db.getPlaceKey(lng+""+lat));
	    		p.setAddress(addr);
	    		p.setInfo(info);
	    		p.setName(pName);
	    		db.updatePlace(p);
	    	}
	    	//<deleteLoc><lat>latE6<lng>lngE6
	    	else if (theMessage.startsWith("<deleteLoc>"))
	    	{
	    		int lat = Integer.parseInt(theMessage.substring(
	    				theMessage.indexOf("<lat>")+5,
	    				theMessage.lastIndexOf("<lng>")
	    				).trim());
	    		int lng = Integer.parseInt(theMessage.substring(
	    				theMessage.lastIndexOf("<lng>")+5
	    				).trim());
	    		Place p = db.getPlace(db.getPlaceKey(lng+""+lat));
	    		db.deletePlace(p);
	    	}
	    	else if (theMessage.startsWith("<like>"))
	    	{
	    		int lat = Integer.parseInt(theMessage.substring(
	    				theMessage.lastIndexOf("<lat>")+5,
	    				theMessage.lastIndexOf("<lng>")
	    				).trim());
	    		int lng = Integer.parseInt(theMessage.substring(
	    				theMessage.indexOf("<lng>")+5
	    				).trim());
	    		db.likePlace(lng+""+lat, name);
	    		
	    	} else if (theMessage.startsWith("<dislike>"))
	    	{
	    		int lat = Integer.parseInt(theMessage.substring(
	    				theMessage.lastIndexOf("<lat>")+5,
	    				theMessage.lastIndexOf("<lng>")
	    				).trim());
	    		int lng = Integer.parseInt(theMessage.substring(
	    				theMessage.indexOf("<lng>")+5
	    				).trim());
	    		
	    		db.dislikePlace(lng+""+lat, name);
	    	} else {
	    		try {
	    			JSONObject jObject = new JSONObject(theMessage.trim());
	    			String id = jObject.getString("id");
	    			if (id.equals("SettingState"))
	    			{
	    				UserConfig uConfig = db.getUserConfig(name);
	    				uConfig.setLoadAround(jObject.getBoolean("near"));
	    				uConfig.setLoadFavorite(jObject.getBoolean("favorite"));
	    				db.updateUserConfig(uConfig);
	    			} else if (id.equals("SettingGeneral"))
	    			{
	    				UserConfig uConfig = db.getUserConfig(name);
	    				uConfig.setAlertChat(jObject.getInt("alertChat"));
	    				uConfig.setAlertPlace(jObject.getInt("alertPlace"));
	    				JSONArray js = new JSONArray(jObject.getString("listType"));
	    				List<String> lsType = new ArrayList<String>();
	    				for (int i = 0 ; i < js.length() ; i++)
	    				{
	    					JSONObject object = js.getJSONObject(i);
	    					lsType.add(object.getString("type"));
	    				}
	    				uConfig.setListType(lsType);
	    				db.updateUserConfig(uConfig);
	    			}
	    		} catch (JSONException e)
	    		{
	    			
	    		}
	    	}
	    }
	  }
	  @Override
	  public void onEmptyPayload(WebConnectionServer server,
	      ServerEndpoint socket, HttpServletRequest req) {
	    
	    // This is the first communication, so we ask for a name once
	    if (socket.getProperty("namerequested", null) == null) {
	      socket.setProperty("namerequested", "true");
	      //socket.send("SUBMITNAME");
	    }
	  }
	  @Override
	  public void init() throws ServletException {
	    super.init();
	    
	    // Persist communication data in the store 
	    // under a particular socket name
	    data = new DatastorePersistence("Socket");
	    
	    // Build a connection server around that server
	    server = WebConnectionServer.fromPeristence(data);
	  }
	
	
}
